﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Globalization;

namespace Xceed.Silverlight.Data
{
  internal static class AsyncResultsExtensions
  {
    #region Async execution extensions
    internal static AsyncResult CastAsyncResult( this IAsyncResult asyncResult )
    {
      AsyncResult asyncResultImpl = asyncResult as AsyncResult;

      if( asyncResultImpl == null )
        ThrowAsyncValidationError( "The IAsyncResult is not an AsyncResult instance." );

      return asyncResultImpl;
    }

    internal static AsyncResultBase CastAsyncResultBase( this IAsyncResult asyncResult )
    {
      AsyncResultBase asyncResultImpl = asyncResult as AsyncResultBase;

      if( asyncResultImpl == null )
        ThrowAsyncValidationError( "The IAsyncResult is not an AsyncResultBase instance." );

      return asyncResultImpl;
    }

    internal static ContextAsyncResult CastContextAsyncResult( this IAsyncResult asyncResult )
    {
      ContextAsyncResult asyncResultImpl = asyncResult as ContextAsyncResult;

      if( asyncResultImpl == null )
        ThrowAsyncValidationError( "The IAsyncResult is not a ContextAsyncResult instance." );

      return asyncResultImpl;
    }

    public static T CastIAsyncResult<T>( this IAsyncResult asyncResult ) where T : IAsyncResult
    {
      if( !typeof( T ).IsAssignableFrom( asyncResult.GetType() ) )
        ThrowAsyncValidationError( string.Format( "The IAsyncResult is not a {0} instance.", typeof( T ).Name ) );

      return ( T )asyncResult;
    }

    public static void ValidateCallEnd( this AsyncResultBase asyncResult, object callId )
    {
      if( !asyncResult.UseCallId( callId ) )
        ThrowAsyncValidationError( "The reference does not belong to the expected call." );

      if( !asyncResult.IsCompleted )
        ThrowAsyncValidationError( "The operation is not complete." );
    }

    public static void ValidateCallEnd( this IAsyncResult asyncResult, object callId )
    {
      AsyncResultBase asyncResultImpl = asyncResult.CastAsyncResultBase();
      asyncResultImpl.ValidateCallEnd( callId );
    }

    public static bool UseCallId( this IAsyncResult asyncResult, object callId )
    {
      AsyncResultBase asyncResultImpl = asyncResult as AsyncResultBase;

      return ( asyncResultImpl != null )
        && asyncResultImpl.UseCallId( callId );
    }

    public static bool UseCallId( this AsyncResultBase asyncResult, object callId )
    {
      bool isSameCallId = ( asyncResult.CallId == null && callId == null )
              || ( asyncResult.CallId != null && asyncResult.CallId.Equals( callId ) );

      return isSameCallId;
    }

    public static T GetLastAsyncResult<T>( this IAsyncResult parentAsyncResult ) where T : IAsyncResult
    {
      AsyncResult asyncResult = parentAsyncResult.CastAsyncResult();
      string wrongTypeName = null;

      if( asyncResult.LastIAsyncResult == null )
      {
        wrongTypeName = "null";
      }
      else if( !typeof( T ).IsAssignableFrom( asyncResult.LastIAsyncResult.GetType() ) )
      {
        wrongTypeName = asyncResult.LastIAsyncResult.GetType().Name;
      }

      if( wrongTypeName != null )
        ThrowAsyncValidationError( string.Format( CultureInfo.CurrentCulture, "The child AsyncResult is not of the expected type. Expected={0}, Actual={1} )", typeof( T ), wrongTypeName ) );

      return ( T )asyncResult.LastIAsyncResult;
    }

    public static T GetCallContext<T>( this IAsyncResult parentAsyncResult )
    {
      ContextAsyncResult asyncResult = parentAsyncResult.CastContextAsyncResult();
      string wrongTypeName = null;

      if( !typeof( T ).IsAssignableFrom( asyncResult.CallContext.GetType() ) )
      {
        wrongTypeName = asyncResult.CallContext.GetType().Name;
      }

      if( wrongTypeName != null )
        ThrowAsyncValidationError( string.Format( CultureInfo.CurrentCulture, "The CallContext is not of the expected type. Expected={0}, Actual={1} )", typeof( T ), wrongTypeName ) );

      return ( T )asyncResult.CallContext;
    }



    #endregion Async execution extensions

    #region AsyncResult Util Properties

    public static int GetMoveUnit( this MoveAndFetchAsyncResult asyncResult )
    {
      return DataUtils.GetUnit( asyncResult.ExpectedMoveCount );
    }

    public static int GetFetchUnit( this MoveAndFetchAsyncResult asyncResult )
    {
      return DataUtils.GetUnit( asyncResult.ExpectedFetchCount );
    }

    public static bool IsMovingForward( this MoveAndFetchAsyncResult asyncResult )
    {
      if( asyncResult.ExpectedMoveCount == 0 )
        throw new InvalidOperationException( "No moves were made with this operation." );

      return ( asyncResult.ExpectedMoveCount > 0 );
    }

    public static bool IsFetchingForward( this MoveAndFetchAsyncResult asyncResult )
    {
      if( asyncResult.ExpectedFetchCount == 0 )
        throw new InvalidOperationException( "No fetches were made with this operation." );

      return ( asyncResult.ExpectedFetchCount > 0 );
    }

    public static JumpMoveAndFetchParameters GetParameters( this JumpMoveAndFetchAsyncResult asyncResult )
    {
      return new JumpMoveAndFetchParameters(
        asyncResult.SourcePosition,
        asyncResult.ExpectedMoveCount,
        asyncResult.ExpectedFetchCount,
        asyncResult.Context );
    }

    #endregion AsyncResult Util Properties

    #region Sync execution extensions

    internal static AsyncResult ExecuteSync( Guid callId, AsyncCallback userCallback, object asyncState, DataEnumeratorResult result )
    {
      AsyncResult asyncResult = new AsyncResult( callId, userCallback, asyncState );
      asyncResult.StartSync( result );
      return asyncResult;
    }

    public static T GetCallResult<T>( this IAsyncResult asyncResult )
    {
      AsyncResult asyncResultImpl = asyncResult.CastAsyncResult();

      if( asyncResultImpl.LastResultData != null )
      {
        if( !typeof( T ).IsAssignableFrom( asyncResultImpl.LastResultData.GetType() ) )
          ThrowAsyncValidationError( "The call result is not of the expected type." );
      }

      return ( T )asyncResultImpl.LastResultData;
    }


    public static T EnsureSyncness<T>( this T asyncResult ) where T : IAsyncResult
    {
      if( asyncResult == null )
        throw new ArgumentNullException( "asyncResult" );

      if( !asyncResult.IsCompleted || !asyncResult.CompletedSynchronously )
        throw new InvalidOperationException( "The child must always execute synchronously." );

      return asyncResult;
    }

    #endregion Sync execution extensions

    private static void ThrowAsyncValidationError( string details )
    {
      if( details == null )
        details = string.Empty;

      throw new ArgumentException( string.Format( CultureInfo.CurrentCulture, "The specified IAsyncResult reference is invalid. {0}", details ) );
    }
  }
}
